package com.zhongkun.datahome.service.service.impl;

import cn.hutool.core.comparator.NullComparator;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Pair;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zhongkun.datahome.common.exception.ServiceException;
import com.zhongkun.datahome.common.utils.SecurityUtils;
import com.zhongkun.datahome.common.utils.StringUtils;
import com.zhongkun.datahome.common.utils.bean.BeanConverter;
import com.zhongkun.datahome.common.utils.bean.BeanUtils;
import com.zhongkun.datahome.framework.redis.RedisLock;
import com.zhongkun.datahome.service.constant.AppointmentStatusEnum;
import com.zhongkun.datahome.service.domain.Appointment;
import com.zhongkun.datahome.service.domain.AppointmentCustomerHouse;
import com.zhongkun.datahome.service.domain.AppointmentRecord;
import com.zhongkun.datahome.service.domain.AppointmentTimeSlot;
import com.zhongkun.datahome.service.domain.CustomerHouse;
import com.zhongkun.datahome.service.export.handler.CustomCellStyleStrategy;
import com.zhongkun.datahome.service.export.handler.DynamicColumnWidthStrategy;
import com.zhongkun.datahome.service.export.handler.MergeCellHandler;
import com.zhongkun.datahome.service.mapper.AppointmentCustomerHouseMapper;
import com.zhongkun.datahome.service.mapper.AppointmentMapper;
import com.zhongkun.datahome.service.mapper.AppointmentRecordMapper;
import com.zhongkun.datahome.service.mapper.AppointmentTimeSlotMapper;
import com.zhongkun.datahome.service.repository.AppointmentRecordRepository;
import com.zhongkun.datahome.service.repository.AppointmentRepository;
import com.zhongkun.datahome.service.repository.AppointmentTimeSlotRepository;
import com.zhongkun.datahome.service.repository.CustomerHouseRepository;
import com.zhongkun.datahome.service.req.cmd.appointment.AppointmentAddCmd;
import com.zhongkun.datahome.service.req.cmd.appointment.AppointmentCancelCmd;
import com.zhongkun.datahome.service.req.cmd.appointment.AppointmentCustomerHouseCmd;
import com.zhongkun.datahome.service.req.cmd.appointment.AppointmentJoinCmd;
import com.zhongkun.datahome.service.req.cmd.appointment.AppointmentRecordViewCmd;
import com.zhongkun.datahome.service.req.cmd.appointment.AppointmentTimeSlotCmd;
import com.zhongkun.datahome.service.req.cmd.appointment.AppointmentUpdateCmd;
import com.zhongkun.datahome.service.req.cmd.appointment.AppointmentViewCmd;
import com.zhongkun.datahome.service.req.qry.appointment.AppointmentQry;
import com.zhongkun.datahome.service.res.PageVo;
import com.zhongkun.datahome.service.res.appointment.AppointmentExportVo;
import com.zhongkun.datahome.service.res.appointment.AppointmentRecordVo;
import com.zhongkun.datahome.service.res.appointment.AppointmentTimeSlotExportVo;
import com.zhongkun.datahome.service.res.appointment.AppointmentVo;
import com.zhongkun.datahome.service.res.house.CustomerHouseDetailVo;
import com.zhongkun.datahome.service.res.house.CustomerHouseVo;
import com.zhongkun.datahome.service.service.IAppointmentService;
import com.zhongkun.datahome.service.service.ICustomerHouseService;
import io.jsonwebtoken.lang.Collections;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @Author: MinKun
 * @Date: Created in 2025/6/26
 * @Description:
 */
@Service
public class AppointmentServiceImpl implements IAppointmentService {

    @Resource
    private ICustomerHouseService customerHouseService;

    @Resource
    private CustomerHouseRepository customerHouseRepository;

    @Resource
    private AppointmentMapper appointmentMapper;

    @Resource
    private AppointmentRepository appointmentRepository;

    @Resource
    private AppointmentRecordRepository appointmentRecordRepository;

    @Resource
    private AppointmentTimeSlotMapper appointmentTimeSlotMapper;

    @Resource
    private AppointmentTimeSlotRepository appointmentTimeSlotRepository;

    @Resource
    private AppointmentCustomerHouseMapper appointmentCustomerHouseMapper;

    @Resource
    private AppointmentRecordMapper appointmentRecordMapper;

    private static final String[] WEEK_DAY = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};

    @Override
    @Transactional
    public boolean addAppointment(AppointmentAddCmd cmd) {
        boolean addSuccess = false;
        Appointment appointment = new Appointment();
        BeanUtils.copyProperties(cmd, appointment);
        appointment.setCreateBy(SecurityUtils.getTenantUserId());
        appointment.setTenantId(SecurityUtils.getTenantId());
        int addCount = appointmentMapper.insert(appointment);
        if (addCount > 0) {
            List<AppointmentTimeSlotCmd> times = cmd.getTimes();
            if (CollectionUtils.isNotEmpty(times)) {
                for (AppointmentTimeSlotCmd time : times) {
                    AppointmentTimeSlot appointmentTimeSlot = new AppointmentTimeSlot();
                    BeanUtils.copyProperties(time, appointmentTimeSlot);
                    appointmentTimeSlot.setAppointmentId(appointment.getId());
                    appointmentTimeSlot.setCreateBy(SecurityUtils.getTenantUserId());
                    appointmentTimeSlot.setTenantId(SecurityUtils.getTenantId());
                    appointmentTimeSlotMapper.insert(appointmentTimeSlot);
                }
            }
            addAppointmentCustomerHouse(cmd.getCustomers(), appointment);
            addSuccess = true;
        }
        return addSuccess;
    }

    @Override
    @Transactional(readOnly = true)
    public boolean checkCustom(AppointmentAddCmd cmd) {
        boolean check = true;
        List<AppointmentCustomerHouseCmd> customers = cmd.getCustomers();
        List<Long> houseIds = customers.stream().map(AppointmentCustomerHouseCmd::getCustomerHouseId).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(customers)) {
            List<Appointment> appointments = appointmentMapper.queryAppointmentByTime(cmd.getEffectiveTimeStart(), cmd.getEffectiveTimeEnd());
            if (!CollectionUtils.isEmpty(appointments)) {
                for (Appointment appointment : appointments) {
                    List<AppointmentCustomerHouse> customerHouses = appointmentCustomerHouseMapper.selectByAppointmentId(appointment.getId());
                    if (!CollectionUtils.isEmpty(customerHouses)) {
                        for (AppointmentCustomerHouse appointmentCustomerHouse : customerHouses) {
                            if (houseIds.contains(appointmentCustomerHouse.getCustomerHouseId())) {
                                check = false;
                            }
                        }
                    }
                }
            }
        }
        return check;
    }

    @Override
    public boolean checkUpdateCustom(AppointmentUpdateCmd cmd) {
        boolean check = true;
        List<AppointmentCustomerHouseCmd> customers = cmd.getCustomers();
        List<Long> houseIds = customers.stream().map(AppointmentCustomerHouseCmd::getCustomerHouseId).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(customers)) {
            List<Appointment> appointments = appointmentMapper.queryAppointmentByTime(cmd.getEffectiveTimeStart(), cmd.getEffectiveTimeEnd());
            if (!CollectionUtils.isEmpty(appointments)) {
                for (Appointment appointment : appointments) {
                    if (appointment.getId().equals(cmd.getId())) {
                        continue;
                    }
                    List<AppointmentCustomerHouse> customerHouses = appointmentCustomerHouseMapper.selectByAppointmentId(appointment.getId());
                    if (!CollectionUtils.isEmpty(customerHouses)) {
                        for (AppointmentCustomerHouse appointmentCustomerHouse : customerHouses) {
                            if (houseIds.contains(appointmentCustomerHouse.getCustomerHouseId())) {
                                check = false;
                            }
                        }
                    }
                }
            }
        }
        return check;
    }

    private void addAppointmentCustomerHouse(List<AppointmentCustomerHouseCmd> customers, Appointment appointment) {
        if (CollectionUtils.isEmpty(customers)) {
            return;
        }
        List<Long> customerHouseIdList = customers.stream().map(AppointmentCustomerHouseCmd::getCustomerHouseId).collect(Collectors.toList());
        List<CustomerHouse> customerHouseList = customerHouseRepository.listByHouseIdList(customerHouseIdList);
        Map<Long, CustomerHouse> customerHouseMap = customerHouseList.stream().collect(Collectors.toMap(CustomerHouse::getId, Function.identity()));
        for (AppointmentCustomerHouseCmd customer : customers) {
            CustomerHouse customerHouse = customerHouseMap.get(customer.getCustomerHouseId());
            if (customerHouse != null) {
                AppointmentCustomerHouse appointmentCustomerHouse = new AppointmentCustomerHouse();
                appointmentCustomerHouse.setAppointmentId(appointment.getId());
                appointmentCustomerHouse.setBuildingNo(customerHouse.getBuildingNum());
                appointmentCustomerHouse.setRoomNo(customerHouse.getRoomNum());
                appointmentCustomerHouse.setCustomerHouseId(customer.getCustomerHouseId());
                appointmentCustomerHouse.setCreateBy(SecurityUtils.getTenantUserId());
                appointmentCustomerHouse.setTenantId(SecurityUtils.getTenantId());
                appointmentCustomerHouseMapper.insert(appointmentCustomerHouse);
            }
        }
    }

    @Override
    @Transactional
    public boolean updateAppointment(AppointmentUpdateCmd cmd) {
        boolean updateSuccess = false;
        Appointment appointment = appointmentMapper.selectById(cmd.getId());
        if (appointment != null) {
            BeanUtils.copyProperties(cmd, appointment);
            appointment.setUpdateBy(SecurityUtils.getTenantUserId());
            int updateCount = appointmentMapper.updateById(appointment);
            updateSuccess = updateCount > 0;

            appointmentTimeSlotMapper.deleteByAppointmentId(appointment.getId());
            List<AppointmentTimeSlotCmd> times = cmd.getTimes();
            if (!CollectionUtils.isEmpty(times)) {
                for (AppointmentTimeSlotCmd time : times) {
                    AppointmentTimeSlot appointmentTimeSlot = new AppointmentTimeSlot();
                    BeanUtils.copyProperties(time, appointmentTimeSlot);
                    appointmentTimeSlot.setAppointmentId(appointment.getId());
                    appointmentTimeSlot.setCreateBy(SecurityUtils.getTenantUserId());
                    appointmentTimeSlot.setTenantId(SecurityUtils.getTenantId());
                    appointmentTimeSlotMapper.insert(appointmentTimeSlot);
                }
            }

            appointmentCustomerHouseMapper.deleteByAppointmentId(appointment.getId());
            addAppointmentCustomerHouse(cmd.getCustomers(), appointment);
        }
        return updateSuccess;
    }

    @Override
    public PageVo<AppointmentVo> page(AppointmentQry qry) {
        // 根据查询条件构建分页查询参数
        IPage<AppointmentVo> page = new Page<>(qry.getPageNo(), qry.getPageSize());
        IPage<AppointmentVo> pageResult = appointmentRepository.pageAppointment(page, qry);
        // 构建并返回分页结果
        return new PageVo<>(pageResult.getRecords(), qry.getPageNo(), qry.getPageSize(), pageResult.getTotal());
    }


    @Override
    @Transactional(readOnly = true)
    public AppointmentViewCmd queryAppointmentView(Long id, Long houseId) {
        Appointment appointment = appointmentMapper.selectById(id);
        if (appointment == null) {
            throw new ServiceException("预约记录不存在");
        }
        AppointmentViewCmd appointmentViewCmd = new AppointmentViewCmd();
        BeanUtils.copyProperties(appointment, appointmentViewCmd);

        List<AppointmentTimeSlot> appointmentTimeSlots = appointmentTimeSlotMapper.selectByAppointmentId(id);
        if (!CollectionUtils.isEmpty(appointmentTimeSlots)) {
            List<AppointmentTimeSlotCmd> times = new ArrayList<>();
            for (AppointmentTimeSlot appointmentTimeSlot : appointmentTimeSlots) {
                AppointmentTimeSlotCmd time = new AppointmentTimeSlotCmd();
                BeanUtils.copyProperties(appointmentTimeSlot, time);
                if (!StringUtils.isEmpty(time.getStartTime())) {
                    time.setStartTime(time.getStartTime().substring(0, time.getStartTime().lastIndexOf(":")));
                }
                if (!StringUtils.isEmpty(time.getEndTime())) {
                    time.setEndTime(time.getEndTime().substring(0, time.getEndTime().lastIndexOf(":")));
                }
                List<AppointmentRecord> appointmentRecords = appointmentRecordMapper.queryByAppointmentIdAndTimeSlotId(id, appointmentTimeSlot.getId());
                if (!CollectionUtils.isEmpty(appointmentRecords)) {
                    List<Long> customerHouseIdList = appointmentRecords.stream().map(AppointmentRecord::getCustomerHouseId).collect(Collectors.toList());
                    List<CustomerHouseVo> customerHouseVoList = customerHouseRepository.getBaseMapper().listByCustomerHouseIdList(customerHouseIdList);
                    Map<Long, CustomerHouseVo> customerHouseMap = customerHouseVoList.stream().collect(Collectors.toMap(CustomerHouse::getId, Function.identity()));

                    List<AppointmentRecordViewCmd> records = new ArrayList<>();
                    for (AppointmentRecord appointmentRecord : appointmentRecords) {
                        CustomerHouseVo customerHouseVo = customerHouseMap.get(appointmentRecord.getCustomerHouseId());
                        if (customerHouseVo != null) {
                            AppointmentRecordViewCmd record = new AppointmentRecordViewCmd();
                            record.setAppointmentId(id);
                            record.setTimeSlotId(appointmentTimeSlot.getId());
                            record.setCustomerHouseId(appointmentRecord.getCustomerHouseId());
                            record.setVisitNum(appointmentRecord.getVisitNum());
                            record.setBuildingNum(customerHouseVo.getBuildingNum());
                            record.setRoomNum(customerHouseVo.getRoomNum());
                            record.setCustomerName(customerHouseVo.getCustomerName());
                            record.setMobile(customerHouseVo.ofMobile());
                            records.add(record);
                        }
                        // 判断是否是我的预约
                        if (houseId != null && !time.getMyAppointment()) {
                            if (appointmentRecord.getCustomerHouseId().longValue() == houseId.longValue()) {
                                time.setMyAppointment(true);
                            }
                        }
                    }
                    time.setRecords(records);
                    time.setCurrentSlots(records.size());
                }
                times.add(time);
            }
            appointmentViewCmd.setTimes(times);
        }

        List<AppointmentCustomerHouse> appointmentCustomerHouses = appointmentCustomerHouseMapper.selectByAppointmentId(id);
        if (!CollectionUtils.isEmpty(appointmentCustomerHouses)) {
            List<AppointmentCustomerHouseCmd> customers = new ArrayList<>();
            for (AppointmentCustomerHouse appointmentCustomerHouse : appointmentCustomerHouses) {
                AppointmentCustomerHouseCmd customer = new AppointmentCustomerHouseCmd();
                BeanUtils.copyProperties(appointmentCustomerHouse, customer);
                customers.add(customer);
            }
            appointmentViewCmd.setCustomers(customers);
        }

        AppointmentRecord appointmentRecord = appointmentRecordRepository.queryAppointmentRecord(houseId, id);
        if (appointmentRecord != null) {
            appointmentViewCmd.setAppointmentRecordVo(BeanConverter.copyBean(AppointmentRecordVo.class, appointmentRecord));
        }
        return appointmentViewCmd;
    }

    @Override
    public AppointmentVo queryMatchAppointment(Long customerHouseId) {
        // 直接通过 SQL 查询活动预约
        Appointment appointment = appointmentRepository.queryMatchAppointment(customerHouseId);
        if (appointment != null) {
            return BeanConverter.copyBean(AppointmentVo.class, appointment);
        }
        return null;
    }

    @Override
    @Transactional
    public void cancelAppointment(AppointmentCancelCmd cmd) {
        AppointmentRecord appointmentRecord = appointmentRecordRepository.getById(cmd.getAppointmentRecordId());
        if (appointmentRecord == null) {
            throw new ServiceException("预约记录不存在");
        }
        appointmentRecord.setAppointmentStatus(AppointmentStatusEnum.CANCEL.getCode());
        appointmentRecordRepository.updateById(appointmentRecord);

        cmd.setTimeSlotId(appointmentRecord.getTimeSlotId());
        ((IAppointmentService) AopContext.currentProxy()).updateTimeSlot(appointmentRecord);
    }

    @RedisLock
    public void updateTimeSlot(AppointmentRecord appointmentRecord) {
        // 根据预约ID和时间槽ID查询对应的预约时间槽
        AppointmentTimeSlot timeSlot = appointmentTimeSlotMapper.selectById(appointmentRecord.getTimeSlotId());
        timeSlot.setCurrentSlots(timeSlot.getCurrentSlots() - 1);
        appointmentTimeSlotMapper.updateById(timeSlot);
    }

    @Override
    public void export(AppointmentQry qry, HttpServletResponse response) {
        IPage<AppointmentVo> page = new Page<>(qry.getPageNo(), 10000);
        IPage<AppointmentVo> pageResult = appointmentRepository.pageAppointment(page, qry);
        List<AppointmentVo> recordList = pageResult.getRecords();
        if (Collections.isEmpty(recordList)) {
            throw new ServiceException("没有满足的数据");
        }

        List<Long> appointmentIdList = recordList.stream().map(AppointmentVo::getId).collect(Collectors.toList());
        List<AppointmentTimeSlot> appointmentTimeSlotList = appointmentTimeSlotRepository.queryByAppointmentIdList(appointmentIdList);

        List<Long> appointmentTimeSlotIdList = appointmentTimeSlotList.stream().map(AppointmentTimeSlot::getId).collect(Collectors.toList());
        List<AppointmentRecord> appointmentRecordList = appointmentRecordRepository.queryByAppointmentTimeSlotIdList(appointmentTimeSlotIdList);
        List<AppointmentRecordVo> appointmentRecordVoList = BeanConverter.convertList(appointmentRecordList, AppointmentRecordVo.class);

        // 填充手机号
        fillRecordVo(appointmentRecordVoList);

        List<AppointmentExportVo> appointmentExportVoList = AppointmentExportVo.build(recordList, appointmentTimeSlotList, appointmentRecordVoList);

        // 获取表头，这里新增了一个单独的时间列表，用于匹配对应日期的客户预约数据
        Pair<List<List<String>>, List<String>> excelHeaderPair = getExcelHeader(appointmentExportVoList);

        // 导出表数据
        List<List<String>> excelBodyList = getExcelBody(appointmentExportVoList, excelHeaderPair.getValue());

        String fileName = "预约列表.xlsx";
        if (qry.getId() != null) {
            fileName = appointmentExportVoList.get(0).getAppointmentName() + "明细.xlsx";
        }
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        try {
            response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));

            MergeCellHandler mergeHandler = new MergeCellHandler(excelBodyList);
            // 1. 创建 ExcelWriter 实例（保存实例）
            ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
                    .head(excelHeaderPair.getKey()) // 动态表头
                    .registerWriteHandler(new DynamicColumnWidthStrategy()) // 动态列宽
                    .registerWriteHandler(mergeHandler) // 合并处理器
                    .registerWriteHandler(new CustomCellStyleStrategy().getCustomerHouseExcelStrategy()) // 自定义样式
                    .build();

            try {
                // 2. 写入数据（指定 Sheet 名称）
                excelWriter.write(excelBodyList, EasyExcel.writerSheet("Sheet1").build());

                // 3. 获取 WriteSheetHolder（关键步骤）
                WriteSheetHolder writeSheetHolder = excelWriter.writeContext().writeSheetHolder();

                // 4. 调用 finishMerge 完成合并（处理最后一行未合并的区域）
                mergeHandler.finishMerge(writeSheetHolder);

            } finally {
                // 5. 关闭写入器（释放资源）
                excelWriter.finish();
            }

        } catch (IOException e) {
            throw new ServiceException("导出失败");
        }
    }

    private void fillRecordVo(List<AppointmentRecordVo> appointmentRecordVoList) {
        if (CollectionUtils.isNotEmpty(appointmentRecordVoList)) {
            List<Long> customerHouseIdList = appointmentRecordVoList.stream().map(AppointmentRecordVo::getCustomerHouseId).collect(Collectors.toList());

            List<CustomerHouseVo> customerHouseVoList = customerHouseRepository.getBaseMapper().listByCustomerHouseIdList(customerHouseIdList);
            Map<Long, CustomerHouseVo> customerHouseMap = customerHouseVoList.stream().collect(Collectors.toMap(CustomerHouse::getId, Function.identity()));

            for (AppointmentRecordVo appointmentRecordVo : appointmentRecordVoList) {
                CustomerHouseVo customerHouseVo = customerHouseMap.get(appointmentRecordVo.getCustomerHouseId());
                if (customerHouseVo == null) {
                    continue;
                }
                appointmentRecordVo.setHouseName(customerHouseVo.getBuildingNum() + "#" + customerHouseVo.getRoomNum());
                appointmentRecordVo.setCustomerName(customerHouseVo.getCustomerName());
                appointmentRecordVo.setCustomerPhone(customerHouseVo.ofMobile());
            }
        }
    }

    /**
     * 直接把时间铺开一个自定义的list
     * 直接把数据铺开，直接把客户信息写到对应的时间列里面
     *
     * @param appointmentExportVoList
     * @param dateList
     * @return
     */
    private List<List<String>> getExcelBody(List<AppointmentExportVo> appointmentExportVoList, List<String> dateList) {
        List<List<String>> excelBodyList = new ArrayList<>();
        //appointmentExportVoList排序
        for (AppointmentExportVo appointmentExportVo : appointmentExportVoList) {
            Map<String, List<AppointmentTimeSlotExportVo>> slotInfoMap = appointmentExportVo.getAppointmentTimeSlotExportVoList().stream().collect(Collectors.groupingBy(AppointmentTimeSlotExportVo::getTimeSlotInfo));
            List<String> slotInfoList = slotInfoMap.keySet().stream().sorted().collect(Collectors.toList());
            for (String slotInfo : slotInfoList) {
                List<String> bodyList = new ArrayList<>();
                bodyList.add(appointmentExportVo.getAppointmentName());
                bodyList.add(appointmentExportVo.getAppointmentHouseInfo());
                bodyList.add(slotInfo);
                List<AppointmentTimeSlotExportVo> appointmentTimeSlotExportItemList = slotInfoMap.get(slotInfo);
                Map<String, AppointmentTimeSlotExportVo> daySlotInfoMap = appointmentTimeSlotExportItemList.stream().collect(Collectors.toMap(x -> DateUtil.format(x.getTimeSlotDate(), "MM月dd日"), Function.identity()));
                for (String date : dateList) {
                    if (daySlotInfoMap.containsKey(date)) {
                        bodyList.add(daySlotInfoMap.get(date).getCustomerInfo());
                    } else {
                        bodyList.add("");
                    }
                }
                excelBodyList.add(bodyList);
            }
        }
        return excelBodyList;
    }

    private Pair<List<List<String>>, List<String>> getExcelHeader(List<AppointmentExportVo> appointmentExportVoList) {
        List<List<String>> headerList = new ArrayList<>();
        List<String> dateList = new ArrayList<>();

        headerList.add(List.of("预约名称"));
        headerList.add(List.of("指定业主"));
        headerList.add(List.of("时间段"));

        List<Date> appointmentDateList = appointmentExportVoList.stream().map(AppointmentExportVo::getAppointmentTimeSlotExportVoList).flatMap(List::stream).map(AppointmentTimeSlotExportVo::getTimeSlotDate).distinct().sorted().collect(Collectors.toList());
        for (Date date : appointmentDateList) {
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(date);
            int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
            String weekday = WEEK_DAY[dayOfWeek - 1];
            String dateStr = DateUtil.format(date, "MM月dd日");
            headerList.add(List.of(dateStr + "\n" + weekday));
            dateList.add(dateStr);
        }
        return Pair.of(headerList, dateList);
    }

    @Override
    @Transactional
    public void deleteAppointment(Long id) {
        appointmentRepository.removeById(id);
        appointmentTimeSlotRepository.deleteByAppointmentId(id);
    }

    @Override
    @Transactional
    @RedisLock
    public void joinAppointment(AppointmentJoinCmd cmd) {
        if (cmd.getOriginRecordId() != null) {
            //取消原来的活动
            cancelAppointment(new AppointmentCancelCmd(cmd.getOriginRecordId()));
        }
        // 根据预约ID和时间槽ID查询对应的预约时间槽
        AppointmentTimeSlot timeSlot = appointmentTimeSlotMapper.selectById(cmd.getTimeSlotId());
        if (timeSlot == null || timeSlot.getCurrentSlots().equals(timeSlot.getMaxSlots())) {
            // 获取锁失败
            throw new ServiceException("当前时段已经约满，请重新选择时段");
        }

        // 创建预约记录
        AppointmentRecord record = new AppointmentRecord();
        record.setAppointmentId(cmd.getAppointmentId());
        record.setTimeSlotId(cmd.getTimeSlotId());
        record.setCustomerHouseId(cmd.getCustomerHouseId());
        record.setVisitNum(cmd.getVisitNum());
        record.setAppointmentStatus(AppointmentStatusEnum.VALID.getCode());
        // 插入预约记录
        int insertCount = appointmentRecordMapper.insert(record);
        if (insertCount > 0) {
            timeSlot.setCurrentSlots(timeSlot.getCurrentSlots() + 1);
            appointmentTimeSlotMapper.updateById(timeSlot);
        }
    }
}
